home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 2003 August / MW 8 2003 CD1.iso / Inside Macworld / Product News / gimp-1.2.4.sit / gimp-1.2.4 / plug-ins / common / edge.c < prev    next >
Encoding:
C/C++ Source or Header  |  2003-05-16  |  19.4 KB  |  728 lines

  1. /* edge filter for the GIMP
  2.  *  -Peter Mattis
  3.  *
  4.  * This filter performs edge detection on the input image.
  5.  *  The code for this filter is based on "pgmedge", a program
  6.  *  that is part of the netpbm package.
  7.  */
  8.  
  9. /* pgmedge.c - edge-detect a portable graymap
  10. **
  11. ** Copyright (C) 1989 by Jef Poskanzer.
  12. **
  13. */
  14.  
  15. /*
  16.  *  Ported to GIMP Plug-in API 1.0
  17.  *  version 1.07
  18.  *  This version requires GIMP v0.99.10 or above.
  19.  *
  20.  *  This plug-in performs edge detection. The code is based on edge.c
  21.  *  for GIMP 0.54 by Peter Mattis.
  22.  *
  23.  *    Eiichi Takamori <taka@ma1.seikyou.ne.jp>
  24.  *    http://ha1.seikyou.ne.jp/home/taka/gimp/
  25.  *
  26.  *  Tips: you can enter arbitrary value into entry.
  27.  *    (not bounded between 1.0 and 10.0)
  28.  *
  29.  *  Changes from version 1.06 to version 1.07:
  30.  *  - Added entry
  31.  *  - Cleaned up code a bit
  32.  *
  33.  *  Differences from Peter Mattis's original `edge' plug-in:
  34.  *    - Added Wrapmode. (useful for tilable images)
  35.  *    - Enhanced speed in this version.
  36.  *    - It works with the alpha channel.
  37.  */
  38.  
  39. #include "config.h"
  40.  
  41. #include <stdio.h>
  42. #include <stdlib.h>
  43.  
  44. #include <gtk/gtk.h>
  45.  
  46. #include <libgimp/gimp.h>
  47. #include <libgimp/gimpui.h>
  48.  
  49. #include "libgimp/stdplugins-intl.h"
  50.  
  51.  
  52. #ifdef RCSID
  53. static gchar rcsid[] = "$Id: edge.c,v 1.18.2.2 2003/05/16 20:46:23 bolsh Exp $";
  54. #endif
  55.  
  56. /* Some useful macros */
  57.  
  58. #define TILE_CACHE_SIZE 48
  59.  
  60. enum
  61. {
  62.   WRAP,
  63.   SMEAR,
  64.   BLACK
  65. };
  66.  
  67. typedef struct
  68. {
  69.   gdouble amount;
  70.   gint    wrapmode;
  71. } EdgeVals;
  72.  
  73. typedef struct
  74. {
  75.   gint run;
  76. } EdgeInterface;
  77.  
  78. typedef struct
  79. {
  80.   GimpTile     *tile;
  81.   gint         row, col;    /* tile's row, col */
  82.   gint       bpp;
  83.   gint       tile_width, tile_height;
  84.   GimpDrawable *drawable;
  85.   gint       drawable_width, drawable_height;
  86. } TileBuf;
  87.  
  88. /*
  89.  * Function prototypes.
  90.  */
  91.  
  92. static void      query  (void);
  93. static void      run    (gchar    *name,
  94.              gint      nparams,
  95.              GimpParam   *param,
  96.              gint     *nreturn_vals,
  97.              GimpParam  **return_vals);
  98.  
  99. static void      edge        (GimpDrawable *drawable);
  100. static gint      edge_dialog (GimpDrawable *drawable);
  101.  
  102. static long      long_sqrt   (long n);
  103.  
  104. static void   init_tile_buf  (TileBuf   *buf,
  105.                   GimpDrawable *drawable);
  106. static void   get_tile_pixel (TileBuf   *buf,
  107.                   gint       x,
  108.                   gint       y, 
  109.                   guchar    *pixel,
  110.                   gint       wrapmode);
  111. static void   end_tile_buf   (TileBuf   *buf);
  112.  
  113. /***** Local vars *****/
  114.  
  115. GimpPlugInInfo PLUG_IN_INFO =
  116. {
  117.   NULL,  /* init  */
  118.   NULL,  /* quit  */
  119.   query, /* query */
  120.   run,   /* run   */
  121. };
  122.  
  123. static EdgeVals evals =
  124. {
  125.   2.0,   /* amount   */
  126.   SMEAR  /* wrapmode */
  127. };
  128.  
  129. static EdgeInterface eint =
  130. {
  131.   FALSE  /* run */
  132. };
  133.  
  134. /***** Functions *****/
  135.  
  136. MAIN ()
  137.  
  138. static void
  139. query (void)
  140. {
  141.   static GimpParamDef args[] =
  142.   {
  143.     { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
  144.     { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
  145.     { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
  146.     { GIMP_PDB_FLOAT, "amount", "Edge detection amount" },
  147.     { GIMP_PDB_INT32, "wrapmode", "Edge detection behavior: { WRAP (0), SMEAR (1), BLACK (2) }" }
  148.   };
  149.   static gint nargs = sizeof (args) / sizeof (args[0]);
  150.  
  151.   gchar *help_string =
  152.     "Perform edge detection on the contents of the specified drawable. It "
  153.     "applies, I think, convolution with 3x3 kernel. AMOUNT is an arbitrary "
  154.     "constant, WRAPMODE is like displace plug-in (useful for tilable image).";
  155.  
  156.   gimp_install_procedure ("plug_in_edge",
  157.               "Perform edge detection on the contents of the specified drawable",
  158.               help_string,
  159.               "Peter Mattis & (ported to 1.0 by) Eiichi Takamori",
  160.               "Peter Mattis",
  161.               "1996",
  162.               N_("<Image>/Filters/Edge-Detect/Edge..."),
  163.               "RGB*, GRAY*",
  164.               GIMP_PLUGIN,
  165.               nargs, 0,
  166.               args, NULL);
  167. }
  168.  
  169. static void
  170. run (gchar  *name,
  171.      gint    nparams,
  172.      GimpParam  *param,
  173.      gint   *nreturn_vals,
  174.      GimpParam **return_vals)
  175. {
  176.   static GimpParam values[1];
  177.   GimpDrawable *drawable;
  178.   GimpRunModeType run_mode;
  179.   GimpPDBStatusType status = GIMP_PDB_SUCCESS;
  180.  
  181.   run_mode = param[0].data.d_int32;
  182.  
  183.   /*  Get the specified drawable  */
  184.   drawable = gimp_drawable_get (param[2].data.d_drawable);
  185.  
  186.   *nreturn_vals = 1;
  187.   *return_vals = values;
  188.  
  189.   values[0].type = GIMP_PDB_STATUS;
  190.   values[0].data.d_status = status;
  191.  
  192.   switch (run_mode)
  193.     {
  194.     case GIMP_RUN_INTERACTIVE:
  195.       INIT_I18N_UI();
  196.       /*  Possibly retrieve data  */
  197.       gimp_get_data ("plug_in_edge", &evals);
  198.  
  199.       /*  First acquire information with a dialog  */
  200.       if (! edge_dialog (drawable))
  201.     return;
  202.       break;
  203.  
  204.     case GIMP_RUN_NONINTERACTIVE:
  205.       /*  Make sure all the arguments are there!  */
  206.       if (nparams != 5)
  207.     status = GIMP_PDB_CALLING_ERROR;
  208.       if (status == GIMP_PDB_SUCCESS)
  209.     {
  210.       evals.amount   = param[3].data.d_float;
  211.       evals.wrapmode = param[4].data.d_int32;
  212.     }
  213.       INIT_I18N();
  214.       break;
  215.  
  216.     case GIMP_RUN_WITH_LAST_VALS:
  217.       /*  Possibly retrieve data  */
  218.       gimp_get_data ("plug_in_edge", &evals);
  219.       INIT_I18N();
  220.       break;
  221.  
  222.     default:
  223.       break;
  224.     }
  225.  
  226.   /* make sure the drawable exist and is not indexed */
  227.   if (gimp_drawable_is_rgb (drawable->id) ||
  228.       gimp_drawable_is_gray (drawable->id))
  229.     {
  230.       gimp_progress_init (_("Edge Detection..."));
  231.  
  232.       /*  set the tile cache size  */
  233.       gimp_tile_cache_ntiles (TILE_CACHE_SIZE);
  234.  
  235.       /*  run the edge effect  */
  236.       edge (drawable);
  237.  
  238.       if (run_mode != GIMP_RUN_NONINTERACTIVE)
  239.     gimp_displays_flush ();
  240.  
  241.       /*  Store data  */
  242.       if (run_mode == GIMP_RUN_INTERACTIVE)
  243.     gimp_set_data ("plug_in_edge", &evals, sizeof (EdgeVals));
  244.     }
  245.   else
  246.      {
  247.       /* gimp_message ("edge: cannot operate on indexed color images"); */
  248.       status = GIMP_PDB_EXECUTION_ERROR;
  249.     }
  250.  
  251.   values[0].data.d_status = status;
  252.  
  253.   gimp_drawable_detach (drawable);
  254. }
  255.  
  256. /*********************************************************************
  257.  
  258.    TileBuf Util Routines:   Util routines for getting arbitrary pixel
  259.    CAUTION -- the tile is read only !!
  260.  
  261.  **********************************************************************/
  262.  
  263. static void
  264. init_tile_buf (TileBuf   *buf,
  265.            GimpDrawable *drawable)
  266. {
  267.   buf->tile = NULL;
  268.   buf->col = 0;
  269.   buf->row = 0;
  270.   if (gimp_drawable_is_rgb (drawable->id))
  271.     buf->bpp = 3;
  272.   else
  273.     buf->bpp = 1;
  274.   buf->tile_width = gimp_tile_width();
  275.   buf->tile_height = gimp_tile_height();
  276.   buf->drawable = drawable;
  277.   buf->drawable_width = gimp_drawable_width(drawable->id);
  278.   buf->drawable_height = gimp_drawable_height(drawable->id);
  279. }
  280.  
  281. static void
  282. get_tile_pixel (TileBuf *buf,
  283.         gint     x,
  284.         gint     y,
  285.         guchar  *pixel,
  286.         gint     wrapmode)
  287. {
  288.   gint b;
  289.   gint offx, offy;
  290.   gint row, col;
  291.   guchar *ptr;
  292.  
  293.   if (x < 0 || x >= buf->drawable_width ||
  294.       y < 0 || y >= buf->drawable_height)
  295.     switch (wrapmode)
  296.       {
  297.       case WRAP:
  298.     if (x < 0 || x >= buf->drawable_width)
  299.       {
  300.         x %= buf->drawable_width;
  301.         if (x < 0)
  302.           x += buf->drawable_width;
  303.       }
  304.     if (y < 0 || y >= buf->drawable_height)
  305.       {
  306.         y %= buf->drawable_height;
  307.         if (y < 0)
  308.           y += buf->drawable_height;
  309.       }
  310.     break;
  311.       case SMEAR:
  312.     if (x < 0)
  313.       x = 0;
  314.     if (x >= buf->drawable_width)
  315.       x = buf->drawable_width - 1;
  316.     if (y < 0)
  317.       y = 0;
  318.     if (y >= buf->drawable_height)
  319.       y = buf->drawable_height - 1;
  320.     break;
  321.       case BLACK:
  322.     if (x < 0 || x >= buf->drawable_width || 
  323.         y < 0 || y >= buf->drawable_height)
  324.       {
  325.         for (b = 0; b < buf->bpp; b++)
  326.           pixel[b] = 0;
  327.         return;
  328.       }
  329.     break;
  330.       default:
  331.     return;
  332.       }
  333.  
  334.   col = x / buf->tile_width;
  335.   offx = x % buf->tile_width;
  336.   row = y / buf->tile_height;
  337.   offy = y % buf->tile_height;
  338.  
  339.   /* retrieve tile */
  340.   if (!buf->tile || col != buf->col || row != buf->row)
  341.     {
  342.       if(buf->tile)
  343.     gimp_tile_unref (buf->tile, FALSE);
  344.       buf->col = col;
  345.       buf->row = row;
  346.       buf->tile = gimp_drawable_get_tile (buf->drawable, FALSE, row, col);
  347.       gimp_tile_ref (buf->tile);
  348.     }
  349.  
  350.   /* retrieve target pixel */
  351.   ptr = buf->tile->data + (offy * buf->tile->ewidth + offx) * buf->tile->bpp;
  352.   for(b = 0; b < buf->bpp; b++)
  353.     pixel[b] = ptr[b];
  354. }
  355.  
  356. static void
  357. end_tile_buf (TileBuf *buf)
  358. {
  359.   if (buf->tile)
  360.     gimp_tile_unref (buf->tile, FALSE);
  361. }
  362.  
  363. /**********************************************************************
  364.    TileBuf Util Routines End
  365.  **********************************************************************/
  366.  
  367. static long
  368. long_sqrt (long n)
  369. {
  370. #define lsqrt_max4pow (1UL << 30)
  371.   /* lsqrt_max4pow is the (machine-specific) largest power of 4 that can
  372.    * be represented in an unsigned long.
  373.    *
  374.    * Compute the integer square root of the integer argument n
  375.    * Method is to divide n by x computing the quotient x and remainder r
  376.    * Notice that the divisor x is changing as the quotient x changes
  377.    * 
  378.    * Instead of shifting the dividend/remainder left, we shift the
  379.    * quotient/divisor right. The binary point starts at the extreme
  380.    * left, and shifts two bits at a time to the extreme right.
  381.    * 
  382.    * The residue contains n-x^2. (Within these comments, the ^ operator
  383.    * signifies exponentiation rather than exclusive or. Also, the /
  384.    * operator returns fractions, rather than truncating, so 1/4 means
  385.    * one fourth, not zero.)
  386.    * 
  387.    * Since (x + 1/2)^2 == x^2 + x + 1/4,
  388.    * n - (x + 1/2)^2 == (n - x^2) - (x + 1/4)
  389.    * Thus, we can increase x by 1/2 if we decrease (n-x^2) by (x+1/4)
  390.    */
  391.  
  392.   gulong residue;        /* n - x^2  */
  393.   gulong root;           /* x + 1/4  */
  394.   gulong half;           /* 1/2      */
  395.  
  396.   residue = n;           /* n - (x = 0)^2, with suitable alignment */
  397.  
  398.   /*
  399.    * if the correct answer fits in two bits, pull it out of a magic hat
  400.    */
  401.   if (residue <= 12)
  402.     return (0x03FFEA94 >> (residue *= 2)) & 3;
  403.  
  404.   root = lsqrt_max4pow;         /* x + 1/4, shifted all the way left */
  405.   /* half = root + root; 1/2, shifted likewise */
  406.  
  407.   /* 
  408.    * Unwind iterations corresponding to leading zero bits 
  409.    */
  410.   while (root > residue)
  411.     root >>= 2;
  412.  
  413.   /*
  414.    * Unwind the iteration corresponding to the first one bit
  415.    * Operations have been rearranged and combined for efficiency
  416.    * Initialization of half is folded into this iteration
  417.    */
  418.   residue -= root;              /* Decrease (n-x^2) by (0+1/4)             */
  419.   half = root >> 2;             /* 1/4, with binary point shifted right 2  */
  420.   root += half;                 /* x=1. (root is now (x=1)+1/4.)           */
  421.   half += half;                 /* 1/2, properly aligned                   */
  422.  
  423.   /*
  424.    * Normal loop (there is at least one iteration remaining)
  425.    */
  426.   do
  427.     {
  428.       if (root <= residue)      /* Whenever we can,                          */
  429.         {
  430.           residue -= root;      /* decrease (n-x^2) by (x+1/4)               */
  431.           root += half;         /* increase x by 1/2                         */
  432.         }
  433.       half >>= 2;               /* Shift binary point 2 places right          */
  434.       root -= half;             /* x{ +1/2 } +1/4 - 1/8 == x { +1/2 } 1/8     */
  435.       root >>= 1;               /* 2x{ +1 } +1/4, shifted right 2 places      */
  436.     }
  437.   while (half);                 /* When 1/2 == 0, bin. point is at far right  */
  438.  
  439.   /* 
  440.    * round up if (x+1/2)^2 < n
  441.    */
  442.   if (root < residue)
  443.     ++root;
  444.  
  445.   /* 
  446.    * Guaranteed to be correctly rounded (or truncated)
  447.    */
  448.   return root;
  449. }
  450.  
  451. /********************************************************/
  452. /*              Edge Detection main                     */
  453. /********************************************************/
  454.  
  455. static void
  456. edge (GimpDrawable *drawable)
  457. {
  458.   /*
  459.    * this function is too long, so I must split this into a few
  460.    * functions later ...  -- taka
  461.    */
  462.   GimpPixelRgn src_rgn, dest_rgn;
  463.   gpointer pr;
  464.   TileBuf buf;
  465.   guchar *srcrow, *src;
  466.   guchar *destrow, *dest;
  467.   guchar pix00[3], pix01[3], pix02[3];
  468.   guchar pix10[3],/*pix11[3],*/ pix12[3];
  469.   guchar pix20[3], pix21[3], pix22[3];
  470.   glong width, height;
  471.   gint alpha, has_alpha, chan;
  472.   gint x, y;
  473.   gint x1, y1, x2, y2;
  474.   glong sum1, sum2;
  475.   glong sum, scale;
  476.   gint maxval;
  477.   gint cur_progress;
  478.   gint max_progress;
  479.   gint wrapmode;
  480.  
  481.   if (evals.amount < 1.0)
  482.     evals.amount = 1.0;
  483.  
  484.   init_tile_buf (&buf, drawable);
  485.  
  486.   gimp_drawable_mask_bounds (drawable->id, &x1, &y1, &x2, &y2);
  487.  
  488.   width = gimp_drawable_width (drawable->id);
  489.   height = gimp_drawable_height (drawable->id);
  490.   alpha = gimp_drawable_bpp (drawable->id);
  491.   has_alpha = gimp_drawable_has_alpha (drawable->id);
  492.   if (has_alpha)
  493.     alpha--;
  494.  
  495.   maxval = 255;
  496.   scale = (10 << 16) / evals.amount;
  497.   wrapmode = evals.wrapmode;
  498.  
  499.   cur_progress = 0;
  500.   max_progress = (x2 - x1) * (y2 - y1);
  501.  
  502.   gimp_pixel_rgn_init (&src_rgn, drawable, x1, y1, x2-x1, y2-y1, FALSE, FALSE);
  503.   gimp_pixel_rgn_init (&dest_rgn, drawable, x1, y1, x2-x1, y2-y1, TRUE, TRUE);
  504.  
  505.   for (pr = gimp_pixel_rgns_register (2, &src_rgn, &dest_rgn); 
  506.        pr != NULL;
  507.        pr = gimp_pixel_rgns_process (pr))
  508.     {
  509.       srcrow = src_rgn.data;
  510.       destrow = dest_rgn.data;
  511.       for (y = dest_rgn.y;
  512.         y < (dest_rgn.y + dest_rgn.h);
  513.         y++, srcrow += src_rgn.rowstride, destrow += dest_rgn.rowstride)
  514.     {
  515.       src = srcrow;
  516.       dest = destrow;
  517.       for (x = dest_rgn.x;
  518.            x < (dest_rgn.x + dest_rgn.w);
  519.            x++,  src += src_rgn.bpp, dest += dest_rgn.bpp)
  520.         {
  521.           if(dest_rgn.x < x &&  x < dest_rgn.x + dest_rgn.w - 1 &&
  522.          dest_rgn.y < y &&  y < dest_rgn.y + dest_rgn.h - 1)
  523.         {
  524.           /*
  525.           ** 3x3 kernel is inside of the tile -- do fast
  526.           ** version
  527.           */
  528.           for (chan = 0; chan < alpha; chan++)
  529.             {
  530.               /*
  531.                * PIX(1,1) is the current pixel, so
  532.                * e.g. PIX(0,0) means 1 above and 1 left pixel.
  533.                *
  534.                * There were casting to `long' in GIMP 0.54
  535.                * edge code, but I think `guchar' should be
  536.                * extended to `int' with minus operators, so
  537.                * there's no need to cast to `long'. Both sum1
  538.                * and sum2 will be between -4*255 to 4*255
  539.                *
  540.                *    -- taka
  541.                */
  542. #define PIX(X,Y)  src[ (Y-1)*(int)src_rgn.rowstride + (X-1)*(int)src_rgn.bpp + chan ]
  543.               /* make convolution */
  544.               sum1 = (PIX(2,0) - PIX(0,0)) +
  545.              2 * (PIX(2,1) - PIX(0,1)) +
  546.                  (PIX(2,2) - PIX(0,2));
  547.               sum2 = (PIX(0,2) - PIX(0,0)) +
  548.              2 * (PIX(1,2) - PIX(1,0)) +
  549.                  (PIX(2,2) - PIX(2,0));
  550. #undef  PIX
  551.               /* common job ... */
  552.               sum = long_sqrt ((long) sum1 * sum1 + (long) sum2 * sum2);
  553.               sum = (sum * scale) >> 16;    /* arbitrary scaling factor */
  554.               if (sum > maxval)
  555.             sum = maxval;
  556.               dest[chan] = sum;
  557.             }
  558.         }
  559.           else
  560.         {
  561.           /*
  562.           ** The kernel is not inside of the tile -- do slow
  563.           ** version
  564.           */
  565.           /*
  566.            * When the kernel intersects the boundary of the
  567.            * image, get_tile_pixel() will (should) do the
  568.            * right work with `wrapmode'.
  569.            */
  570.           get_tile_pixel (&buf, x-1, y-1, pix00, wrapmode);
  571.           get_tile_pixel (&buf, x  , y-1, pix10, wrapmode);
  572.           get_tile_pixel (&buf, x+1, y-1, pix20, wrapmode);
  573.           get_tile_pixel (&buf, x-1, y  , pix01, wrapmode);
  574.           get_tile_pixel (&buf, x+1, y  , pix21, wrapmode);
  575.           get_tile_pixel (&buf, x-1, y+1, pix02, wrapmode);
  576.           get_tile_pixel (&buf, x  , y+1, pix12, wrapmode);
  577.           get_tile_pixel (&buf, x+1, y+1, pix22, wrapmode);
  578.  
  579.           for (chan = 0; chan < alpha; chan++)
  580.             {
  581.               /* make convolution */
  582.               sum1 = (pix20[chan] - pix00[chan]) +
  583.              2 * (pix21[chan] - pix01[chan]) +
  584.                  (pix22[chan] - pix20[chan]);
  585.               sum2 = (pix02[chan] - pix00[chan]) +
  586.              2 * (pix12[chan] - pix10[chan]) +
  587.                  (pix22[chan] - pix20[chan]);
  588.               /* common job ... */
  589.               sum = long_sqrt ((long) sum1 * sum1 + (long) sum2 * sum2);
  590.               sum = (sum * scale) >> 16;  /* arbitrary scaling factor */
  591.               if (sum > maxval) sum = maxval;
  592.               dest[chan] = sum;
  593.             }
  594.         }
  595.           if (has_alpha)
  596.         dest[alpha] = src[alpha];
  597.         }
  598.         }
  599.       cur_progress += dest_rgn.w * dest_rgn.h;
  600.       gimp_progress_update ((double) cur_progress / (double) max_progress);
  601.     }
  602.  
  603.   end_tile_buf (&buf);
  604.   gimp_drawable_flush (drawable);
  605.   gimp_drawable_merge_shadow (drawable->id, TRUE);
  606.   gimp_drawable_update (drawable->id, x1, y1, (x2 - x1), (y2 - y1));
  607. }
  608.  
  609. /*******************************************************/
  610. /*                    Dialog                           */
  611. /*******************************************************/
  612.  
  613. static void
  614. edge_ok_callback (GtkWidget *widget,
  615.           gpointer   data)
  616. {
  617.   eint.run = TRUE;
  618.  
  619.   gtk_widget_destroy (GTK_WIDGET (data));
  620. }
  621.  
  622. static gint
  623. edge_dialog (GimpDrawable *drawable)
  624. {
  625.   GtkWidget *dlg;
  626.   GtkWidget *frame;
  627.   GtkWidget *table;
  628.   GtkWidget *hbox;
  629.   GtkWidget *toggle;
  630.   GtkObject *scale_data;
  631.   GSList *group = NULL;
  632.  
  633.   gint    use_wrap  = (evals.wrapmode == WRAP);
  634.   gint    use_smear = (evals.wrapmode == SMEAR);
  635.   gint    use_black = (evals.wrapmode == BLACK);
  636.  
  637.   gimp_ui_init ("edge", FALSE);
  638.  
  639.   dlg = gimp_dialog_new (_("Edge Detection"), "edge",
  640.              gimp_standard_help_func, "filters/edge.html",
  641.              GTK_WIN_POS_MOUSE,
  642.              FALSE, TRUE, FALSE,
  643.  
  644.              _("OK"), edge_ok_callback,
  645.              NULL, NULL, NULL, TRUE, FALSE,
  646.              _("Cancel"), gtk_widget_destroy,
  647.              NULL, 1, NULL, FALSE, TRUE,
  648.  
  649.              NULL);
  650.  
  651.   gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
  652.               GTK_SIGNAL_FUNC (gtk_main_quit),
  653.               NULL);
  654.  
  655.   /*  parameter settings  */
  656.   frame = gtk_frame_new (_("Parameter Settings"));
  657.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  658.   gtk_container_set_border_width (GTK_CONTAINER (frame), 6);
  659.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0);
  660.  
  661.   table = gtk_table_new (2, 3, FALSE);
  662.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  663.   gtk_table_set_row_spacings (GTK_TABLE (table), 4);
  664.   gtk_container_set_border_width (GTK_CONTAINER (table), 4);
  665.   gtk_container_add (GTK_CONTAINER (frame), table);
  666.  
  667.   /*  Label, scale, entry for evals.amount  */
  668.   scale_data = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
  669.                      _("Amount:"), 100, 0,
  670.                      evals.amount, 1.0, 10.0, 0.1, 1.0, 1,
  671.                      TRUE, 0, 0,
  672.                      NULL, NULL);
  673.  
  674.   gtk_signal_connect (GTK_OBJECT (scale_data), "value_changed",
  675.               GTK_SIGNAL_FUNC (gimp_double_adjustment_update),
  676.               &evals.amount);
  677.  
  678.   /*  Radio buttons WRAP, SMEAR, BLACK  */
  679.  
  680.   hbox = gtk_hbox_new (FALSE, 4);
  681.   gtk_table_attach (GTK_TABLE (table), hbox, 0, 3, 1, 2,
  682.             GTK_FILL, GTK_FILL, 0, 0);
  683.  
  684.   toggle = gtk_radio_button_new_with_label (group, _("Wrap"));
  685.   group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle));
  686.   gtk_box_pack_start (GTK_BOX (hbox), toggle, TRUE, TRUE, 0);
  687.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  688.               GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  689.               &use_wrap);
  690.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), use_wrap);
  691.   gtk_widget_show (toggle);
  692.  
  693.   toggle = gtk_radio_button_new_with_label (group, _("Smear"));
  694.   group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle));
  695.   gtk_box_pack_start (GTK_BOX (hbox), toggle, TRUE, TRUE, 0);
  696.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  697.               GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  698.               &use_smear);
  699.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), use_smear);
  700.   gtk_widget_show (toggle);
  701.  
  702.   toggle = gtk_radio_button_new_with_label (group, _("Black"));
  703.   group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle));
  704.   gtk_box_pack_start (GTK_BOX (hbox), toggle, TRUE, TRUE, 0);
  705.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  706.               GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  707.               &use_black);
  708.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), use_black);
  709.   gtk_widget_show (toggle);
  710.   gtk_widget_show (hbox);
  711.  
  712.   gtk_widget_show (table);
  713.   gtk_widget_show (frame);
  714.   gtk_widget_show (dlg);
  715.  
  716.   gtk_main ();
  717.   gdk_flush ();
  718.  
  719.   if (use_wrap)
  720.     evals.wrapmode = WRAP;
  721.   else if (use_smear)
  722.     evals.wrapmode = SMEAR;
  723.   else if (use_black)
  724.     evals.wrapmode = BLACK;
  725.  
  726.   return eint.run;
  727. }
  728.